package com.lingshi.shopping_search_service.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.FieldSuggester;
import co.elastic.clients.elasticsearch.core.search.Suggester;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.elasticsearch.indices.AnalyzeRequest;
import co.elastic.clients.elasticsearch.indices.AnalyzeResponse;
import co.elastic.clients.elasticsearch.indices.analyze.AnalyzeToken;
import co.elastic.clients.json.JsonData;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lingshi.shopping.constant.Constant;
import com.lingshi.shopping.entity.*;
import com.lingshi.shopping.enums.ErrorEnums;
import com.lingshi.shopping.exception.BusException;
import com.lingshi.shopping.request.GoodsSearchParam;
import com.lingshi.shopping.result.GoodsSearchResult;
import com.lingshi.shopping.service.SearchService;
import lombok.SneakyThrows;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.Order;

import java.util.*;
import java.util.stream.Collectors;

@DubboService
public class SearchServiceImpl implements SearchService {


    @Autowired
    private ElasticsearchClient client;


    @Autowired
    private ElasticsearchTemplate template;


    @Override
    @SneakyThrows
    public List<String> autoSuggest(String keyword) {
        //不全搜索对象
        Suggester suggester = Suggester.of(fn -> fn.suggesters("goods_suggestion",
                FieldSuggester.of(fs -> fs.completion(
                        f -> f.skipDuplicates(true)
                                .size(10)
                                .field("tags")
                ))).text(keyword));

        //补全搜索
        SearchResponse<Map> response = client.search(s -> s.index("goods").suggest(suggester), Map.class);

        //获取补全结果
        Map<String, List<Suggestion<Map>>> suggest = response.suggest();

        Suggestion<Map> goodsSuggestion = suggest.get("goods_suggestion").get(0);

        List<CompletionSuggestOption<Map>> options = goodsSuggestion.completion().options();

        //获取补全数据
        List<String> datas = options.stream().map(CompletionSuggestOption::text).toList();


        return datas;
    }

    @Override
    public GoodsSearchResult search(GoodsSearchParam goodsSearchParam) {
        // 1.构造ES搜索条件
        NativeQuery query = buildNativeQuery(goodsSearchParam);

        // 2.搜索
        SearchHits<GoodsES> resultSearchHits = template.search(query, GoodsES.class);

        long total = resultSearchHits.getTotalHits();

        List<SearchHit<GoodsES>> searchHits = resultSearchHits.getSearchHits();
        //当前页数据
        List<GoodsES> goodsESList = searchHits.stream().map(SearchHit::getContent).toList();

        // 3.将查询结果封装为Page对象,统一使用MyBatisPlus的分页对象（为了保证和前端所有分页统一风格）
        Page<GoodsES> page = new Page<>();
        page.setSize(goodsSearchParam.getSize());
        page.setCurrent(goodsSearchParam.getPage());
        page.setTotal(total);
        page.setRecords(goodsESList);

        // 4.封装结果对象
        GoodsSearchResult result = new GoodsSearchResult();
        // 4.1 查询结果
        result.setGoodsPage(page);
        // 4.2 查询查询参数
        result.setGoodsSearchParam(goodsSearchParam);
        // 4.3 查询面板
        buildSearchPanel(goodsSearchParam,result);

        return result;
    }

    /**
     * 构建查询面板
     * @param params
     * @param result
     */
    private void buildSearchPanel(GoodsSearchParam params, GoodsSearchResult result) {
        params.setPage(1);
        params.setSize(100);
        params.setSort(null);
        params.setSortFiled(null);
        NativeQuery query = buildNativeQuery(params);

        SearchHits<GoodsES> resultSearchHits = template.search(query, GoodsES.class);
        //搜索关联度最高的前面20个商品 （品牌）
        List<GoodsES> goodsESList = resultSearchHits.getSearchHits().stream().map(SearchHit::getContent).toList();

        //封装品牌
        Set<String> brands = goodsESList.stream().map(GoodsES::getBrand).collect(Collectors.toSet());
        result.setBrands(brands);
        //封装分类
        HashSet<String> productTypes = new HashSet<>();
        for (GoodsES goodsES : goodsESList) {
            Set<String> productType = goodsES.getProductType();
            productTypes.addAll(productType);
        }
        result.setProductType(productTypes);

        //封装商品规格
        Map<String,Set<String>> specMap = new HashMap<>();
        for (GoodsES goodsES : goodsESList) {
            Map<String, List<String>> specification = goodsES.getSpecification();
            specification.forEach((specName, options) -> {

                if (specMap.containsKey(specName)) {
                    Set<String> values = specMap.get(specName);
                    specMap.get(specName).addAll(values);
                }else{
                    specMap.put(specName, new HashSet<>(options));
                }


            });


        }
        result.setSpecifications(specMap);
    }

    private NativeQuery buildNativeQuery(GoodsSearchParam params) {
        //条件构建对象
        NativeQueryBuilder nativeQueryBuilder = new NativeQueryBuilder();
        //构建多条件对象
        BoolQuery.Builder bool = QueryBuilders.bool();


        //关键字
        if (StrUtil.isNotBlank(params.getKeyword())) {
            MultiMatchQuery matchQuery = MultiMatchQuery.of(fn -> fn.query(params.getKeyword()).fields("goodsName", "caption", "brand"));
            bool.must(matchQuery._toQuery());
        } else {
            throw new BusException(ErrorEnums.NO_KEYWORD_ERROR);
        }
        //品牌
        String brand = params.getBrand();
        if (StrUtil.isNotBlank(brand)) {
            MatchQuery matchQuery = MatchQuery.of(fn -> fn.query(brand).field("brand"));
            bool.must(matchQuery._toQuery());
        }

        //价格
        Double lowPrice = params.getLowPrice();
        Double highPrice = params.getHighPrice();
        if (lowPrice != null && lowPrice > 0) {
            RangeQuery rangeQuery = RangeQuery.of(fn -> fn.field("price").gte(JsonData.of(lowPrice)));
            bool.must(rangeQuery._toQuery());
        }

        if (highPrice != null && highPrice > 0) {
            RangeQuery rangeQuery = RangeQuery.of(fn -> fn.field("price").lte(JsonData.of(highPrice)));
            bool.must(rangeQuery._toQuery());
        }

        //商品规格条件
        Map<String, String> specMap = params.getSpecificationOption();// CPU:晓龙8至尊版
        if (CollUtil.isNotEmpty(specMap)) {
            specMap.forEach((specName, optionName) -> {
                TermQuery termQuery = TermQuery.of(fn -> fn.field("specification." + specName + ".keyword").value(optionName));
                bool.must(termQuery._toQuery());
            });
        }

        //排序
        String sortFiled = params.getSortFiled();//NEW PRICE
        String sort = params.getSort();//ASC DESC
        if (StrUtil.isNotBlank(sort) && StrUtil.isNotBlank(sortFiled)) {
            Sort sortParam = null;
            if (Constant.PRICE.equals(sortFiled)) {
                if (Constant.ASC.equals(sort)) {
                    sortParam = Sort.by(Order.asc("price"));
                }
                if (Constant.DESC.equals(sort)) {
                    sortParam = Sort.by(Order.desc("price"));
                }
            }
            if (Constant.NEW.equals(sort)) {
                if (Constant.ASC.equals(sort)) {
                    sortParam = Sort.by(Order.asc("id"));
                }
                if (Constant.DESC.equals(sort)) {
                    sortParam = Sort.by(Order.desc("id"));
                }
            }
            nativeQueryBuilder.withSort(sortParam);
        }

        //分页
        PageRequest pageRequest = PageRequest.of(params.getPage() - 1, params.getSize());
        nativeQueryBuilder.withPageable(pageRequest);


        nativeQueryBuilder.withQuery(bool.build()._toQuery());
        //构建Query对象
        return nativeQueryBuilder.build();
    }


    @Override
    public void syncGoodsToES(GoodsDesc goodsDesc) {

        GoodsES goodsES = new GoodsES();
        //基本信息
        goodsES.setId(goodsDesc.getId());
        goodsES.setGoodsName(goodsDesc.getGoodsName());
        goodsES.setPrice(goodsDesc.getPrice());
        goodsES.setCaption(goodsDesc.getCaption());
        goodsES.setHeaderPic(goodsDesc.getHeaderPic());
        //品牌
        goodsES.setBrand(goodsDesc.getBrand().getName());
        //标签：搜索使用关键字
        Set<String> tags = new HashSet<>();
        tags.addAll(analyze(goodsDesc.getGoodsName(), "ik_smart"));
        tags.addAll(analyze(goodsDesc.getCaption(), "ik_smart"));
        goodsES.setTags(tags);

        //类目
        Set<String> types = new HashSet<>();
        types.add(goodsDesc.getProductType1().getName());
        types.add(goodsDesc.getProductType2().getName());
        types.add(goodsDesc.getProductType3().getName());

        goodsES.setProductType(types);

        //商品规格  cpu: ["8gen1","8gen2] 屏幕尺寸：【6寸，7寸】
        Map<String, List<String>> specMap = new HashMap<>();
        List<Specification> specifications = goodsDesc.getSpecifications();
        for (Specification specification : specifications) {
            List<String> optionNames = specification.getSpecificationOptions().stream().map(SpecificationOption::getOptionName).toList();
            specMap.put(specification.getSpecName(), optionNames);
        }
        goodsES.setSpecification(specMap);

        //保存到es
        template.save(goodsES);


    }

    @Override
    public void delete(Long id) {
        template.delete(String.valueOf(id),GoodsES.class);
    }

    @Override
    @SneakyThrows
    public List<String> analyze(String keyword, String analyzer) {

        AnalyzeRequest analyzeRequest = AnalyzeRequest.of(fn -> fn.index("goods").analyzer(analyzer).text(keyword));

        AnalyzeResponse analyze = client.indices().analyze(analyzeRequest);

        List<AnalyzeToken> tokens = analyze.tokens();


        List<String> result = tokens.stream().map(AnalyzeToken::token).toList();

        return result;
    }
}
